//
//  UIImage+Lc.m
//  18Birdies
//
//  Created by Lc on 15/4/3.
//  Copyright (c) 2015年 Lc. All rights reserved.
//

#import "LcPreconditions.h"
#import "UIImage+Lc.h"
#import "LcLog.h"

@interface CALayer (UIViewToUIImage)

- (void)applyContentsScaleInAllSubLayers:(CGFloat)scale;

@end

@implementation CALayer (UIViewToUIImage)

- (void)applyContentsScaleInAllSubLayers:(CGFloat)scale {
  self.contentsScale = scale;
  for (CALayer* layer in self.sublayers) {
    [layer applyContentsScaleInAllSubLayers:scale];
  }
}

@end

@implementation UIImage (Lc)

static UIImage* transparentImage;

+ (UIImage*)imageWithColor:(UIColor*)color {
  UIGraphicsBeginImageContext(CGSizeMake(1, 1));
  [color set];
  UIRectFill(CGRectMake(0, 0, 3, 3));
  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return image;
}

+ (UIImage*)imageWithColor:(UIColor*)color andSize:(CGSize)size {
  UIGraphicsBeginImageContext(size);
  [color set];
  // +1 to fix accuracy
  UIRectFill(CGRectMake(0, 0, size.width + 1, size.height + 1));
  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return image;
}

+ (UIImage*)circleImageWithColor:(UIColor*)color andRadius:(CGFloat)radius {
  UIGraphicsBeginImageContextWithOptions(CGSizeMake(radius * 2, radius * 2), NO,
                                         [UIScreen mainScreen].scale);
  CGContextRef ctx = UIGraphicsGetCurrentContext();
  [color set];
  // +1 to fix accuracy
  CGContextAddArc(ctx, radius, radius, radius, 0, 2 * M_PI, 0);
  CGContextFillPath(ctx);
  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return image;
}

+ (UIImage*)linearImageWithStartColor:(UIColor*)startColor
                             endColor:(UIColor*)endColor
                              andSize:(CGSize)size {
  UIGraphicsBeginImageContext(size);
  CGContextRef context = UIGraphicsGetCurrentContext();

  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGFloat locations[] = {0.0, 1.0};
  NSArray* colors = @[ (__bridge id)startColor.CGColor, (__bridge id)endColor.CGColor ];
  CGGradientRef gradient =
      CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)colors, locations);
  CGPoint startPoint = CGPointZero;
  CGPoint endPoint = CGPointMake(0, size.height);
  CGContextSaveGState(context);
  CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0);
  CGContextRestoreGState(context);
  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  CGGradientRelease(gradient);
  CGColorSpaceRelease(colorSpace);

  return image;
}

+ (UIImage*)imageFromUIView:(UIView*)view scale:(CGFloat)scale {
  LC_CHECK_TRUE(scale > 0);
  scale = scale;
  CGSize size = CGSizeMake(view.bounds.size.width * scale, view.bounds.size.height * scale);
  if (size.height * size.width * 8 > 200 * 1024 * 1024) {
    LcLog(@"too large image:%@", [NSValue valueWithCGSize:size]);
    LC_DEBUG_FAIL();
    return nil;
  }
  UIGraphicsBeginImageContextWithOptions(size, NO, 1);
  CGContextRef context = UIGraphicsGetCurrentContext();
  CGContextScaleCTM(context, scale, scale);
  CGFloat originContentsScale = view.layer.contentsScale;
  [view.layer applyContentsScaleInAllSubLayers:scale];
  [view.layer renderInContext:context];
  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  [view.layer applyContentsScaleInAllSubLayers:originContentsScale];
  return image;
}

+ (UIImage*)imageFromUIView:(UIView*)view targetRect:(CGRect)targetRect {
  CGFloat scale = [UIScreen mainScreen].scale;
  CGSize size = CGSizeMake(view.bounds.size.width * scale, view.bounds.size.height * scale);
  UIGraphicsBeginImageContextWithOptions(size, NO, 1);
  CGContextRef context = UIGraphicsGetCurrentContext();
  CGContextScaleCTM(context, scale, scale);
  [view.layer renderInContext:context];
  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  targetRect = CGRectMake(targetRect.origin.x*scale, targetRect.origin.y*scale, targetRect.size.width*scale, targetRect.size.height*scale);
  CGImageRef subImageRef = CGImageCreateWithImageInRect(image.CGImage, targetRect);
  CGRect smallBounds = CGRectMake(0, 0, CGImageGetWidth(subImageRef), CGImageGetHeight(subImageRef));
  UIGraphicsBeginImageContext(smallBounds.size);
  CGContextRef smallContext = UIGraphicsGetCurrentContext();
  CGContextDrawImage(smallContext, smallBounds, subImageRef);
  UIImage* newImage = [UIImage imageWithCGImage:subImageRef];
  UIGraphicsEndImageContext();
  return newImage;
}

+ (UIImage*)snapShotFromUIView:(UIView*)view targetRect:(CGRect)targetRect {
  UIView* newView = [view resizableSnapshotViewFromRect:targetRect afterScreenUpdates:YES withCapInsets:UIEdgeInsetsZero];
  CGSize size = newView.bounds.size;
  UIGraphicsBeginImageContextWithOptions(size, YES, [UIScreen mainScreen].scale);
  CGRect rec = CGRectMake(newView.frame.origin.x, newView.frame.origin.y, newView.bounds.size.width, newView.bounds.size.height);
  [newView drawViewHierarchyInRect:rec afterScreenUpdates:YES];
  UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return image;
}

+ (UIImage*)transparentImage {
  if (transparentImage == nil) {
    UIGraphicsBeginImageContext(CGSizeMake(1, 1));
    transparentImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
  }
  return transparentImage;
}

- (UIImage*)scaleToSize:(CGSize)size {
  LC_CHECK_TRUE(size.width > 0);
  LC_CHECK_TRUE(size.height > 0);
  UIGraphicsBeginImageContext(size);
  [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
  UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();
  return scaledImage;
}

- (UIImage*)scaleWithRatio:(CGFloat)ratio {
  LC_CHECK_TRUE(ratio > 0 && ratio < 1);
  CGSize originalSize = self.size;
  CGSize size = CGSizeMake(floor(originalSize.width * ratio), floor(originalSize.height * ratio));
  return [self scaleToSize:size];
}

- (UIImage*)cropImageWithRect:(CGRect)rect {
  CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, self.size.width * self.scale,
                               self.size.height * self.scale);

  UIGraphicsBeginImageContext(rect.size);
  CGContextRef context = UIGraphicsGetCurrentContext();
  CGContextClearRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));

  [self drawInRect:drawRect];

  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  return image;
}

- (UIImage*)clearImageWithRect:(CGRect)rect {
  UIGraphicsBeginImageContext(self.size);
  CGContextRef context = UIGraphicsGetCurrentContext();

  [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
  CGContextClearRect(context, rect);
  UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  return image;
}

- (UIImage*)imageRotatedByRadians:(CGFloat)radians {
  UIView* rotatedViewBox =
      [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.size.width, self.size.height)];
  CGAffineTransform t = CGAffineTransformMakeRotation(radians);
  rotatedViewBox.transform = t;
  CGSize rotatedSize = rotatedViewBox.frame.size;

  UIGraphicsBeginImageContext(rotatedSize);
  CGContextRef bitmap = UIGraphicsGetCurrentContext();

  CGContextTranslateCTM(bitmap, rotatedSize.width / 2, rotatedSize.height / 2);
  CGContextRotateCTM(bitmap, radians);

  CGContextScaleCTM(bitmap, 1.0, -1.0);
  CGContextDrawImage(bitmap, CGRectMake(-self.size.width / 2, -self.size.height / 2,
                                        self.size.width, self.size.height),
                     [self CGImage]);

  UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
  UIGraphicsEndImageContext();

  return newImage;
}

+ (UIImage*)normalizedImageByOrientationCorrectForImage:(UIImage*)image {
  LC_CHECK_NOT_NULL(image);
  if (image.imageOrientation == UIImageOrientationUp)
    return image;

  CGAffineTransform transform = CGAffineTransformIdentity;

  switch (image.imageOrientation) {
    case UIImageOrientationDown:
    case UIImageOrientationDownMirrored: {
      transform = CGAffineTransformTranslate(transform, image.size.width, image.size.height);
      transform = CGAffineTransformRotate(transform, M_PI);
    } break;
    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored: {
      transform = CGAffineTransformTranslate(transform, image.size.width, 0);
      transform = CGAffineTransformRotate(transform, M_PI_2);
    } break;
    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored: {
      transform = CGAffineTransformTranslate(transform, 0, image.size.height);
      transform = CGAffineTransformRotate(transform, -M_PI_2);
    } break;
    default:
      break;
  }

  switch (image.imageOrientation) {
    case UIImageOrientationUpMirrored:
    case UIImageOrientationDownMirrored: {
      transform = CGAffineTransformTranslate(transform, image.size.width, 0);
      transform = CGAffineTransformScale(transform, -1, 1);
    } break;
    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRightMirrored: {
      transform = CGAffineTransformTranslate(transform, image.size.height, 0);
      transform = CGAffineTransformScale(transform, -1, 1);
    } break;
    default:
      break;
  }

  CGContextRef ctx = CGBitmapContextCreate(
      NULL, image.size.width, image.size.height, CGImageGetBitsPerComponent(image.CGImage), 0,
      CGImageGetColorSpace(image.CGImage), CGImageGetBitmapInfo(image.CGImage));
  CGContextConcatCTM(ctx, transform);
  switch (image.imageOrientation) {
    case UIImageOrientationLeft:
    case UIImageOrientationLeftMirrored:
    case UIImageOrientationRight:
    case UIImageOrientationRightMirrored: {
      CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.height, image.size.width), image.CGImage);
    } break;
    default: {
      CGContextDrawImage(ctx, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
    } break;
  }

  CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
  UIImage* correctedImage = [UIImage imageWithCGImage:cgImage];
  CGContextRelease(ctx);
  CGImageRelease(cgImage);
  return correctedImage;
}

- (UIImage*)maskWithColor:(UIColor*)color {
  CGFloat width = self.size.width * self.scale;
  CGFloat height = self.size.height * self.scale;
  CGRect bounds = CGRectMake(0, 0, width, height);

  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  CGContextRef bitmapContext = CGBitmapContextCreate(NULL, width, height, 8, 0, colorSpace,
                                                     (int)kCGImageAlphaPremultipliedLast);
  CGContextClipToMask(bitmapContext, bounds, self.CGImage);
  CGContextSetFillColorWithColor(bitmapContext, color.CGColor);
  CGContextFillRect(bitmapContext, bounds);

  CGImageRef cImage = CGBitmapContextCreateImage(bitmapContext);
  UIImage* coloredImage =
      [UIImage imageWithCGImage:cImage scale:self.scale orientation:UIImageOrientationUp];
  CGImageRelease(cImage);
  CGContextRelease(bitmapContext);
  CGColorSpaceRelease(colorSpace);

  return coloredImage;
}

+ (UIImage*)imageOfResizingModeTileWithName:(NSString*)imageName {
  return [[UIImage imageNamed:imageName] resizableImageWithCapInsets:UIEdgeInsetsZero
                                                        resizingMode:UIImageResizingModeTile];
}

- (CGSize)scaleToFillLimitSize:(CGSize)limitSize {
  CGFloat imageHeight = self.size.height;
  CGFloat imageWidth = self.size.width;
  LC_CHECK_TRUE(imageHeight > 0 && imageWidth > 0);
  CGFloat limitRadio = imageWidth / imageHeight;
  if (imageHeight <= limitSize.height && imageWidth <= limitSize.width) {
    return self.size;
  } else {
    if (imageHeight > limitSize.height) {
      CGFloat newWidth = limitSize.height * limitRadio;
      if (newWidth >= limitSize.width) {
        return CGSizeMake(limitSize.width, limitSize.width / limitRadio);
      } else {
        return CGSizeMake(newWidth, limitSize.height);
      }
    } else if (imageWidth > limitSize.width) {
      CGFloat newHeight = limitSize.width / limitRadio;
      if (newHeight >= limitSize.height) {
        return CGSizeMake(limitSize.height * limitRadio, limitSize.height);
      } else {
        return CGSizeMake(limitSize.width, newHeight);
      }
    }
  }
  LC_FAIL();
  return CGSizeZero;
}

@end
